]> dgit.raspbian.org Git - gnome-shell.git/commitdiff
Add a new tool, 'gnome-shell-extension-prefs', which can configure extensions
authorJasper St. Pierre <jstpierre@mecheye.net>
Thu, 19 Jan 2012 02:21:56 +0000 (21:21 -0500)
committerJasper St. Pierre <jstpierre@mecheye.net>
Tue, 7 Feb 2012 21:00:37 +0000 (16:00 -0500)
A new tool, 'gnome-shell-extension-prefs' can load a new entry point from
extensions, 'prefs.js', which has an entry point to return a GTK+ widget.
This allows extensions to have their own preferences dialog, without each
extension needing to ship its own Python script and .desktop file.

https://bugzilla.gnome.org/show_bug.cgi?id=668429

.gitignore
data/Makefile.am
data/gnome-shell-extension-prefs.desktop.in.in [new file with mode: 0644]
js/Makefile.am
js/extensionPrefs/main.js [new file with mode: 0644]
js/misc/config.js.in
src/Makefile.am
src/gnome-shell-extension-prefs.in [new file with mode: 0644]

index 4ec7bad8b85222405258a07afaead84c31b8c301..f6dc70b02730ec80e7160c3fc079c6b622c533b8 100644 (file)
@@ -18,6 +18,8 @@ config
 configure
 data/gnome-shell.desktop
 data/gnome-shell.desktop.in
+data/gnome-shell-extension-prefs.desktop
+data/gnome-shell-extension-prefs.desktop.in
 data/gschemas.compiled
 data/org.gnome.shell.gschema.xml
 data/org.gnome.shell.gschema.valid
@@ -62,6 +64,7 @@ src/calendar-server/org.gnome.Shell.CalendarServer.service
 src/gnome-shell
 src/gnome-shell-calendar-server
 src/gnome-shell-extension-tool
+src/gnome-shell-extension-prefs
 src/gnome-shell-hotplug-sniffer
 src/gnome-shell-jhbuild
 src/gnome-shell-perf-helper
index 04c8f4f6ad05c2f43a2f6aaecfa883392f1a9058..c077aa22d133a44c73fc4e1a7ecf3e2426084c65 100644 (file)
@@ -1,5 +1,5 @@
 desktopdir=$(datadir)/applications
-desktop_DATA = gnome-shell.desktop
+desktop_DATA = gnome-shell.desktop gnome-shell-extension-prefs.desktop
 
 # We substitute in bindir so it works as an autostart
 # file when built in a non-system prefix
@@ -69,6 +69,7 @@ shaders_DATA = \
 
 EXTRA_DIST =                                           \
        gnome-shell.desktop.in.in                       \
+       gnome-shell-extension-prefs.in.in               \
        $(menu_DATA)                                    \
        $(shaders_DATA)                                 \
        $(convert_DATA)                                 \
@@ -76,6 +77,7 @@ EXTRA_DIST =                                          \
 
 CLEANFILES =                                           \
        gnome-shell.desktop.in                          \
+       gnome-shell-extension-prefs.in                  \
        $(desktop_DATA)                                 \
        $(gsettings_SCHEMAS)                            \
        gschemas.compiled
diff --git a/data/gnome-shell-extension-prefs.desktop.in.in b/data/gnome-shell-extension-prefs.desktop.in.in
new file mode 100644 (file)
index 0000000..9f8bbf4
--- /dev/null
@@ -0,0 +1,12 @@
+[Desktop Entry]
+Type=Application
+_Name=GNOME Shell Extension Preferences
+_Comment=Configure GNOME Shell Extensions
+Exec=@bindir@/gnome-shell-extension-prefs %u
+X-GNOME-Bugzilla-Bugzilla=GNOME
+X-GNOME-Bugzilla-Product=gnome-shell
+X-GNOME-Bugzilla-Component=extensions
+X-GNOME-Bugzilla-Version=@VERSION@
+Categories=GNOME;GTK;
+OnlyShowIn=GNOME;
+NoDisplay=true
index f7ce7cd6995e7ca861c4a8be5c4923ea4774d60c..4c41bffee812879b5ce6f166b2838d8a554607f3 100644 (file)
@@ -7,6 +7,7 @@ nobase_dist_js_DATA =   \
        gdm/fingerprint.js      \
        gdm/loginDialog.js      \
        gdm/powerMenu.js        \
+       extensionPrefs/main.js  \
        misc/config.js          \
        misc/docInfo.js         \
        misc/extensionUtils.js  \
diff --git a/js/extensionPrefs/main.js b/js/extensionPrefs/main.js
new file mode 100644 (file)
index 0000000..82b67b4
--- /dev/null
@@ -0,0 +1,280 @@
+
+const Lang = imports.lang;
+const Gettext = imports.gettext;
+const GLib = imports.gi.GLib;
+const GObject = imports.gi.GObject;
+const Gio = imports.gi.Gio;
+const Gtk = imports.gi.Gtk;
+const Pango = imports.gi.Pango;
+
+const _ = Gettext.gettext;
+
+const Config = imports.misc.config;
+const Format = imports.misc.format;
+const ExtensionUtils = imports.misc.extensionUtils;
+
+
+const GnomeShellIface = <interface name="org.gnome.Shell">
+<signal name="ExtensionStatusChanged">
+    <arg type="s" name="uuid"/>
+    <arg type="i" name="state"/>
+    <arg type="s" name="error"/>
+</signal>
+</interface>;
+
+const GnomeShellProxy = Gio.DBusProxy.makeProxyWrapper(GnomeShellIface);
+
+function stripPrefix(string, prefix) {
+    if (string.slice(0, prefix.length) == prefix)
+        return string.slice(prefix.length);
+    return string;
+}
+
+const Application = new Lang.Class({
+    Name: 'Application',
+    _init: function() {
+        GLib.set_prgname('gnome-shell-extension-prefs');
+        this.application = new Gtk.Application({
+            application_id: 'org.gnome.shell.ExtensionPrefs',
+            flags: Gio.ApplicationFlags.HANDLES_COMMAND_LINE
+        });
+
+        this.application.connect('activate', Lang.bind(this, this._onActivate));
+        this.application.connect('command-line', Lang.bind(this, this._onCommandLine));
+        this.application.connect('startup', Lang.bind(this, this._onStartup));
+
+        this._extensionMetas = {};
+        this._extensionPrefsModules = {};
+
+        this._extensionIters = {};
+    },
+
+    _buildModel: function() {
+        this._model = new Gtk.ListStore();
+        this._model.set_column_types([GObject.TYPE_STRING, GObject.TYPE_STRING]);
+    },
+
+    _extensionAvailable: function(uuid) {
+        let meta = this._extensionMetas[uuid];
+
+        if (!meta)
+            return false;
+
+        if (ExtensionUtils.isOutOfDate(meta))
+            return false;
+
+        if (!meta.dir.get_child('prefs.js').query_exists(null))
+            return false;
+
+        return true;
+    },
+
+    _setExtensionInsensitive: function(layout, cell, model, iter, data) {
+        let uuid = model.get_value(iter, 0);
+        if (!this._extensionAvailable(uuid))
+            cell.set_sensitive(false);
+    },
+
+    _getExtensionPrefsModule: function(meta) {
+        if (this._extensionPrefsModules.hasOwnProperty(meta.uuid))
+            return this._extensionPrefsModules[meta.uuid];
+
+        ExtensionUtils.installImporter(meta);
+
+        let prefsModule = meta.importer.prefs;
+        prefsModule.init(meta);
+
+        this._extensionPrefsModules[meta.uuid] = prefsModule;
+        return prefsModule;
+    },
+
+    _selectExtension: function(uuid) {
+        if (!this._extensionAvailable(uuid))
+            return;
+
+        let meta = this._extensionMetas[uuid];
+        let widget;
+
+        try {
+            let prefsModule = this._getExtensionPrefsModule(meta);
+            widget = prefsModule.buildPrefsWidget(meta);
+        } catch (e) {
+            widget = this._buildErrorUI(meta, e);
+        }
+
+        // Destroy the current prefs widget, if it exists
+        if (this._extensionPrefsBin.get_child())
+            this._extensionPrefsBin.get_child().destroy();
+
+        this._extensionPrefsBin.add(widget);
+        this._extensionSelector.set_active_iter(this._extensionIters[uuid]);
+    },
+
+    _extensionSelected: function() {
+        let [success, iter] = this._extensionSelector.get_active_iter();
+        if (!success)
+            return;
+
+        let uuid = this._model.get_value(iter, 0);
+        this._selectExtension(uuid);
+    },
+
+    _buildErrorUI: function(meta, exc) {
+        let box = new Gtk.Box({ orientation: Gtk.Orientation.VERTICAL });
+        let label = new Gtk.Label({
+            label: _("There was an error loading the preferences dialog for %s:").format(meta.name)
+        });
+        box.add(label);
+
+        let errortext = '';
+        errortext += exc;
+        errortext += '\n\n';
+        errortext += 'Stack trace:\n';
+
+        // Indent stack trace.
+        errortext += exc.stack.split('\n').map(function(line) {
+            return '  ' + line;
+        }).join('\n');
+
+        let scroll = new Gtk.ScrolledWindow({ vexpand: true });
+        let buffer = new Gtk.TextBuffer({ text: errortext });
+        let textview = new Gtk.TextView({ buffer: buffer });
+        textview.override_font(Pango.font_description_from_string('monospace'));
+        scroll.add(textview);
+        box.add(scroll);
+
+        box.show_all();
+        return box;
+    },
+
+    _buildUI: function(app) {
+        this._window = new Gtk.ApplicationWindow({ application: app,
+                                                   window_position: Gtk.WindowPosition.CENTER,
+                                                   title: _("GNOME Shell Extension Preferences") });
+
+        this._window.set_size_request(600, 400);
+
+        let vbox = new Gtk.Box({ orientation: Gtk.Orientation.VERTICAL });
+        this._window.add(vbox);
+
+        let toolbar = new Gtk.Toolbar();
+        toolbar.get_style_context().add_class(Gtk.STYLE_CLASS_PRIMARY_TOOLBAR);
+        vbox.add(toolbar);
+        let toolitem;
+
+        let label = new Gtk.Label({ label: _("<b>Extension</b>"),
+                                    use_markup: true });
+        toolitem = new Gtk.ToolItem({ child: label });
+        toolbar.add(toolitem);
+
+        this._extensionSelector = new Gtk.ComboBox({ model: this._model,
+                                                     margin_left: 8,
+                                                     hexpand: true });
+        this._extensionSelector.get_style_context().add_class(Gtk.STYLE_CLASS_RAISED);
+
+        let renderer = new Gtk.CellRendererText();
+        this._extensionSelector.pack_start(renderer, true);
+        this._extensionSelector.add_attribute(renderer, 'text', 1);
+        this._extensionSelector.set_cell_data_func(renderer, Lang.bind(this, this._setExtensionInsensitive), null);
+        this._extensionSelector.connect('changed', Lang.bind(this, this._extensionSelected));
+
+        toolitem = new Gtk.ToolItem({ child: this._extensionSelector });
+        toolitem.set_expand(true);
+        toolbar.add(toolitem);
+
+        this._extensionPrefsBin = new Gtk.Frame();
+        vbox.add(this._extensionPrefsBin);
+
+        let label = new Gtk.Label({
+            label: _("Select an extension to configure using the combobox above."),
+            vexpand: true
+        });
+
+        this._extensionPrefsBin.add(label);
+
+        this._shellProxy = new GnomeShellProxy(Gio.DBus.session, 'org.gnome.Shell', '/org/gnome/Shell');
+        this._shellProxy.connectSignal('ExtensionStatusChanged', Lang.bind(this, function(proxy, senderName, [uuid, state, error]) {
+            if (!this._extensionMetas.hasOwnProperty(uuid))
+                this._scanExtensions();
+        }));
+
+        this._window.show_all();
+    },
+
+    _scanExtensions: function() {
+        ExtensionUtils.scanExtensions(Lang.bind(this, function(uuid, dir, type) {
+            if (this._extensionMetas.hasOwnProperty(uuid))
+                return;
+
+            let meta;
+            try {
+                meta = ExtensionUtils.loadMetadata(uuid, dir, type);
+            } catch(e) {
+                global.logError('' + e);
+                return;
+            }
+
+            this._extensionMetas[uuid] = meta;
+
+            let iter = this._model.append();
+            this._model.set(iter, [0, 1], [uuid, meta.name]);
+            this._extensionIters[uuid] = iter;
+        }));
+    },
+
+    _onActivate: function() {
+        this._window.present();
+    },
+
+    _onStartup: function(app) {
+        this._buildModel();
+        this._buildUI(app);
+        this._scanExtensions();
+    },
+
+    _onCommandLine: function(app, commandLine) {
+        app.activate();
+        let args = commandLine.get_arguments();
+        if (args.length) {
+            let uuid = args[0];
+
+            // Strip off "extension:///" prefix which fakes a URI, if it exists
+            uuid = stripPrefix(uuid, "extension:///");
+
+            if (!this._extensionAvailable(uuid))
+                return 1;
+
+            this._selectExtension(uuid);
+        }
+        return 0;
+    }
+});
+
+function initEnvironment() {
+    // Monkey-patch in a "global" object that fakes some Shell utilities
+    // that ExtensionUtils depends on.
+    window.global = {
+        log: function() {
+            print([].join.call(arguments, ', '));
+        },
+
+        logError: function(s) {
+            global.log('ERROR: ' + s);
+        },
+
+        userdatadir: GLib.build_filenamev([GLib.get_user_data_dir(), 'gnome-shell'])
+    };
+
+    String.prototype.format = Format.format;
+}
+
+function main(argv) {
+    initEnvironment();
+    ExtensionUtils.init();
+
+    Gettext.bindtextdomain(Config.GETTEXT_PACKAGE, Config.LOCALEDIR);
+    Gettext.textdomain(Config.GETTEXT_PACKAGE);
+
+    let app = new Application();
+    app.application.run(argv);
+}
index a35fe28b2d58615a6a86746a8cddc8b0e30d8400..6cbf9324d98f932b25c6a19e5cfd9199a80e0fe2 100644 (file)
@@ -10,3 +10,7 @@ const GJS_VERSION = '@GJS_VERSION@';
 const HAVE_BLUETOOTH = @HAVE_BLUETOOTH@;
 /* The system TLS CA list */
 const SHELL_SYSTEM_CA_FILE = '@SHELL_SYSTEM_CA_FILE@';
+/* gettext package */
+const GETTEXT_PACKAGE = '@GETTEXT_PACKAGE@';
+/* locale dir */
+const LOCALEDIR = '@datadir@/locale';
index a46d779b40c3d496000a3b4c04970fc3736f525a..464831a76155054b5515b996d826f854cd0244f9 100644 (file)
@@ -27,8 +27,8 @@ CLEANFILES += $(service_DATA)
 
 CLEANFILES += $(gir_DATA) $(typelib_DATA)
 
-bin_SCRIPTS += gnome-shell-extension-tool
-EXTRA_DIST += gnome-shell-extension-tool.in
+bin_SCRIPTS += gnome-shell-extension-tool gnome-shell-extension-prefs
+EXTRA_DIST += gnome-shell-extension-tool.in gnome-shell-extension-prefs.in
 bin_PROGRAMS = gnome-shell-real
 
 if USE_JHBUILD_WRAPPER_SCRIPT
@@ -52,11 +52,13 @@ generated_script_substitutions = \
            -e "s|@datadir[@]|$(datadir)|g" \
            -e "s|@libexecdir[@]|$(libexecdir)|g" \
            -e "s|@libdir[@]|$(libdir)|g" \
+           -e "s|@pkglibdir[@]|$(pkglibdir)|g" \
            -e "s|@JHBUILD_TYPELIBDIR[@]|$(JHBUILD_TYPELIBDIR)|g" \
            -e "s|@pkgdatadir[@]|$(pkgdatadir)|g" \
            -e "s|@PYTHON[@]|$(PYTHON)|g" \
            -e "s|@VERSION[@]|$(VERSION)|g" \
-           -e "s|@sysconfdir[@]|$(sysconfdir)|g"
+           -e "s|@sysconfdir[@]|$(sysconfdir)|g" \
+           -e "s|@GJS_CONSOLE[@]|$(GJS_CONSOLE)|g"
 
 gnome-shell-jhbuild: gnome-shell-jhbuild.in gnome-shell-real Makefile
        $(AM_V_GEN) sed $(generated_script_substitutions) $< > $@.tmp && mv $@.tmp $@ && chmod a+x $@
@@ -66,6 +68,9 @@ gnome-shell-jhbuild: gnome-shell-jhbuild.in gnome-shell-real Makefile
 gnome-shell-extension-tool: gnome-shell-extension-tool.in Makefile
        $(AM_V_GEN) sed $(generated_script_substitutions) $< > $@.tmp && mv $@.tmp $@ && chmod a+x $@
 
+gnome-shell-extension-prefs: gnome-shell-extension-prefs.in Makefile
+       $(AM_V_GEN) sed $(generated_script_substitutions) $< > $@.tmp && mv $@.tmp $@ && chmod a+x $@
+
 CLEANFILES += gnome-shell $(bin_SCRIPTS)
 
 include Makefile-st.am
diff --git a/src/gnome-shell-extension-prefs.in b/src/gnome-shell-extension-prefs.in
new file mode 100644 (file)
index 0000000..fa37254
--- /dev/null
@@ -0,0 +1,15 @@
+#!/bin/bash
+
+if [ -n "$GI_TYPELIB_PATH" ]; then
+    export GI_TYPELIB_PATH=@pkglibdir@
+else
+    export GI_TYPELIB_PATH=@pkglibdir@:$GI_TYPELIB_PATH
+fi
+
+if [ -n "$LD_LIBRARY_PATH" ] ; then
+    export LD_LIBRARY_PATH=@pkglibdir@
+else
+    export LD_LIBRARY_PATH=@pkglibdir@:$LD_LIBRARY_PATH
+fi
+
+@GJS_CONSOLE@ -I @pkgdatadir@/js -c "const Main = imports.extensionPrefs.main; Main.main(ARGV);" "$@"